GJ.add('app/common/widget/widget.js', ['jquery', 'js/util/event/event_emitter.js'], function (require, exports) {
    var $ = require('jquery');
    var Events = require('js/util/event/event_emitter.js');

    var Widget = exports;

    Widget.ready = function ($el, cb) {
        $el = $($el);

        if (!$el.length) {
            return;
        }

        if (typeof $el.data('widget') !== 'string') {
            cb($el.data('widget'));
        } else {
            if ($el.data('widget-ready-callback-queue')) {
                $el.data('widget-ready-callback-queue').push(cb);
            } else {
                $el.data('widget-ready-callback-queue', [cb]);
            }
        }
    };

    Widget.initWidgets = function () {
        $('[data-widget]').each(function () {
            exports.initWidget($(this));
        });
    };

    Widget.initWidget = function ($el) {
        $el = $($el);
        var config = $el.data();
        config.$el = $el;
        var roles = {};
        // 自动收集元素, 例如: config.$btn
        $el.find('[data-role]').each(function () {
            var role = $(this).data('role');

            if (!roles[role]) {
                roles[role] = [];
            }
            roles[role].push(this);
        });
        GJ.each(roles, function (role, key) {
            config['$'+key] = $(role);
        });

        if(typeof config.widget !== 'string' || $el.data('_loading')) {
            return false;
        }

        // 加载指定的模块进行初始化
        GJ.each(config.widget.split(','), function (url) {
            url = $.trim(url);
            var method = '';
            if (url.indexOf('#') !== -1) {
                url    = url.split('#');
                method = url[1];
                url    = url[0];
            }
            $el.data('_loading', true);
            GJ.use(url, function (widget) {
                if (method) {
                    widget = widget[method];
                }
                try {
                    widget = new widget(config);
                } catch (ex) {
                    GJ.log("Widget init error:[" + url + "#" + method + ']'+ ex.message);
                    var attrs = {
                        tagName: $el.get(0).tagName
                    };
                    $.each($el.get(0).attributes, function () {
                        attrs[this.nodeName] = this.nodeValue;
                    });
                    GJ.errorStack.push({
                        type: 'WIDGET_INIT_ERROR',
                        message: ex.message,
                        url: url + '#' + method,
                        $el: attrs
                    });

                    throw ex;
                }
                $el.data('widget', widget);
                $el.data('_loading', false);
                if ($el.data('widget-ready-callback-queue')) {
                    $.each($el.data('widget-ready-callback-queue'), function (i, cb) {
                        cb(widget);
                    });
                }
            });
        });
    };

    Widget.define = function (def) {
        def = def || {};
        def.events = def.events || {};
        def.init = def.init || function (config) {
            this.$el = $(config.$el);
        };

        $.each(def, function (key, target) {
            if ($.isFunction(target)) {
                def[key] = GJ.guard(target, 'WIDGET');
            }
        });

        var widget = function (config) {
            var self = GJ.mix({}, def);
            Events.mixTo(self);
            if (config.$el) {
                self.$el = $(config.$el);
            }

            self.delegateEvents = function (events) {
                events = events || def.events;
                GJ.each(events, function (cb, key) {
                    var index = key.indexOf(' ');
                    var event = index === -1 ? key : key.substr(0, index);
                    var $el   = index === -1 ? '' : key.substr(index, key.length - 1);

                    if (typeof cb === 'function') {
                        cb = $.proxy(cb, self);
                    } else {
                        cb = $.proxy(self[cb], self);
                    }

                    if ($el) {
                        self.$el.on(event, $el, cb);
                    } else {
                        self.$el.on(event, cb);
                    }
                });
            };

            if (self.$el) {
                self.delegateEvents();
            }
            self.init(config);

            return self;
        };

        widget.extend = function (child) {
            child = $.extend({}, def, child);
            child.super_ = $.extend({}, def);
            child.events = $.extend({}, def.events, child.events);

            return Widget.define(child);
        };

        widget.define = $.extend({}, def);

        return widget;
    };

    // Simple JavaScript Templating
    // John Resig - http://ejohn.org/ - MIT Licensed

    Widget.template = function (str, data){
        // Generate a reusable function that will serve as a template
        // generator (and which will be cached).
        var fn = new Function("obj",
            "var __p=[],print=function(){__p.push.apply(__p,arguments);};" +

            // Introduce the data as local variables using with(){}
            "with(obj){__p.push('" +

            // Convert the template into pure JavaScript
            str
                .replace(/[\r\t\n]/g, " ")
                .split("<%").join("\t")
                .replace(/((^|%>)[^\t]*)'/g, "$1\r")
                .replace(/\t=(.*?)%>/g, "',$1,'")
                .split("\t").join("');")
                .split("%>").join("__p.push('")
                .split("\r").join("\\'") +
            "');}return __p.join('');");

        // Provide some basic currying to the user
        return data ? fn( data ) : fn;
    };
});